package edu.mit.compilers.assembly.x64;

import edu.mit.compilers.assembly.common.AssemblyCodesInfo;
import edu.mit.compilers.assembly.common.AssemblyConvertor;
import edu.mit.compilers.assembly.x64.codes.*;
import edu.mit.compilers.ir.type.IRBasicType;
import edu.mit.compilers.lowercode.*;

public class X64Convertor implements AssemblyConvertor {

    @Override
    public AssemblyCodesInfo convertToAssembly(ThreeAddressCodesInfo info) {
        X64CodeList codes = new X64CodeList();
        X64RuntimeChecker.fillErrorInfoIntoSymbolTable(info.symbolTable);
        fillMethodName(info.symbolTable);
        generateDataArea(info.symbolTable, codes);
        generateTextArea(info, codes);
        return new X64CodesInfo(codes);
    }

    private void fillMethodName(SymbolTable symbolTable) {
        for (String methodName : symbolTable.getMethodNames()) {
            Variable variable = new Variable(X64Util.builtInMethodName(methodName), true);
            variable.setConstValue(new Value('"' + methodName + '"'));
            FieldInfo fieldInfo = new FieldInfo(IRBasicType.StringType, variable);
            symbolTable.putConstStringInfo(fieldInfo);
        }
    }

    private void generateDataArea(SymbolTable symbolTable, X64CodeList codes) {
        // init global field
        codes.append(X64FlagCode.DATA_SEC);
        for (FieldInfo fieldInfo : symbolTable.getGlobalFieldsInfo().values()) {
            long size = fieldInfo.getSize();
            codes.append(new X64GlobalDataCode(fieldInfo.getName(), size));
        }

        // init (const) string field
        for (FieldInfo fieldInfo : symbolTable.getConstStringInfo().values()) {
            assert fieldInfo.getVariable().hasConstValue();
            codes.append(new X64LabelCode(fieldInfo.getName()));
            codes.append(new X64ConstStringCode(fieldInfo.getVariable().getConstValue().getLiteralString()));
        }
    }

    private void generateTextArea(ThreeAddressCodesInfo info, X64CodeList codes) {
        codes.append(X64FlagCode.TEXT_SEC);
        codes.append(X64FlagCode.GLOBAL_MAIN);

        X64MethodConvertor visitor = new X64MethodConvertor(info, codes);
        for (String methodName : info.method2Codes.keySet()) {
            codes.append(new X64LabelCode(methodName));
            visitor.convertMethod(methodName);
        }

        visitor.finish();
    }

}
